﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;

namespace boe2.irc
{
	public class IrcMessage
    {
        public IrcPrefix Prefix { get; internal set; }
		public MessageType MessageType { get; internal set; }
		public String Message { get; internal set; }
		public String RawMessage { get; internal set; }
		public String[] Parameters { get; internal set; }
		public String Target { get; internal set; }
		public DateTime TimeStamp { get; internal set; }

		public override String ToString() // this method is mainly for debugging purposes. You are expected to write your own message interface
		{
			if (this.Prefix.IsGlobal)
			{
				return "*** " + this.Message;
			}
			switch (this.MessageType)
			{
				case irc.MessageType.PRIVMSG:
				case irc.MessageType.NOTICE:
					return String.Format("<{0}> {1}", this.Prefix.Nick, this.Message);
				case irc.MessageType.JOIN:
					return String.Format("{0} has joined {1}.", this.Prefix.Nick, this.Message);
				case irc.MessageType.PART:
					return String.Format("{0} has left {1}.", this.Prefix.Nick, this.Message);
				case irc.MessageType.ERROR:
					return String.Format("ERROR: {0}", this.Message);
				case irc.MessageType.MODE:
					if (!String.IsNullOrEmpty(Message))
					{
						return String.Format("{0} sets MODE for {1} to {2}.", this.Prefix.Nick, this.Parameters[0], this.Message);
					}
					return String.Format("{0} sets MODE for {1} to {2}.", this.Prefix.Nick, this.Parameters[0], String.Join(" ", Parameters.Where((val, idx) => idx != 0).ToArray()));
				case irc.MessageType.NICK:
					return String.Format("{0} is now known as {1}.", this.Prefix.Nick, this.Message);
				case irc.MessageType.QUIT:
					return String.Format("{0} has quit ({1}).", this.Prefix.Nick, this.Message);
				case irc.MessageType.UNKNOWN:
				default:
					return String.Format("{0}", this.RawMessage);
			}
		}

		public IrcMessage()
		{
		}

        public IrcMessage(String rawMessage, String prefix, String command, String parameters, String message)
        {
			RawMessage = rawMessage;
			Prefix = new IrcPrefix(prefix);
			if (!String.IsNullOrEmpty(parameters))
			{
				Parameters = parameters.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			}
			if (command.Length > 3)
			{
				MessageType type;
				if (!Enum.TryParse<MessageType>(command, out type))
				{
					MessageType = irc.MessageType.UNKNOWN;
				}
				else
				{
					MessageType = type;
				}
			}
			else
			{
				ProcessServerCommand(command);
			}
			Message = message;
			TimeStamp = DateTime.Now;
			GetTarget();
			if (String.IsNullOrEmpty(message))
			{
				message = command + " " + parameters;
			}
        }

        public static IrcMessage[] Parse(String data)
        {
            String[] messages = data.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            IrcMessage[] ircMessages = new IrcMessage[messages.Length];
            for (Int32 i = 0; i < messages.Length; i++)
            {
                ircMessages[i] = ParseSingle(messages[i]);
            }
            return ircMessages;
        }

		public static IrcMessage ParseSingle(String data)
		{
			String prefix = null;
			String command = null;
			String message = null;
			String parameters = null;
			if (!data.StartsWith(":"))
			{
                Int32 spaceIndex = data.IndexOf(' ');
                if (spaceIndex > -1)
                {
                    command = data.Substring(0, spaceIndex);
                    message = data.Substring(data.IndexOf(' ') + 1);
                }
                else
                {
                    message = data;
                }
			}
			else
			{
				Regex regex = new Regex(@":(?<prefix>\S*)\s*(?<command>\S*)\s*(?<parameters>[^:]*)(:(?<message>.*))?");
				Match match = regex.Match(data.Trim());
				if (match.Success)
				{

					prefix = match.Groups["prefix"].Captures.Count > 0 ? match.Groups["prefix"].Captures[0].Value : "";
					command = match.Groups["command"].Captures.Count > 0 ? match.Groups["command"].Captures[0].Value : "";
					parameters = match.Groups["parameters"].Captures.Count > 0 ? match.Groups["parameters"].Captures[0].Value : "";
					message = match.Groups["message"].Captures.Count > 0 ? match.Groups["message"].Captures[0].Value : "";
				}
				else
				{
					command = data;
				}
			}

			return new IrcMessage(data, prefix, command, parameters, message);
		}

		private void ProcessServerCommand(String command)
		{
			switch (command)
			{
				case "332":
					this.MessageType = irc.MessageType.CHANNELTOPIC;
					break;
				case "353":
					this.MessageType = irc.MessageType.USERLIST;
					break;
			}
		}

		private void GetTarget()
		{
			switch (this.MessageType)
			{
				case MessageType.JOIN:
					Target = this.Message;
					this.Message = "";
					break;
				case MessageType.KICK:
				case MessageType.MODE:
				case MessageType.PRIVMSG:
				case MessageType.NOTICE:
				case MessageType.PART:
				case MessageType.TOPIC:
					Target = this.Parameters[0];
					break;
				case MessageType.UNKNOWN:
				default:
					if (this.Parameters != null)
					{
						Target = this.Parameters.Where(p => p.StartsWith("#")).FirstOrDefault();
					}
					break;
			}
		}
    }

    public enum MessageType
    {
		UNKNOWN,
        PING,
        PRIVMSG,
		NOTICE,
		JOIN,
		PART,
		QUIT,
		KICK,
		KILL,
		MODE,
		TOPIC,
		NICK,
		ERROR,
		CHANNELTOPIC,
		USERLIST
    }
}
